package mn.idax.exchange.otc.aspect;

import com.google.common.collect.ImmutableList;
import mn.idax.exchange.otc.annotation.RequestLimit;
import mn.idax.exchange.otc.enums.RedisPrefixKeyEnum;
import mn.idax.exchange.otc.enums.response.SystemStatusResponseEnum;
import mn.idax.exchange.otc.exception.IdaxException;
import mn.idax.exchange.otc.util.RequestLimitUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.io.Serializable;
import java.lang.reflect.Method;

/**
 * @Auther: dison
 * @Date: 2018/8/14 14:02
 * @Description:
 */
@Aspect
@Configuration
public class RequestLimitAspect {


    private final RedisTemplate<String, Serializable> limitRedisTemplate;

    @Autowired
    public RequestLimitAspect(RedisTemplate<String, Serializable> limitRedisTemplate) {
        this.limitRedisTemplate = limitRedisTemplate;
    }
    /**
     *
     * 功能描述:
     *      定义注解切点
     * @param:
     * @return:
     * @auther: dison
     * @date: 2018/8/14 14:05
     */
    @Pointcut("@annotation(requestLimit)")
    public void requestLimitPoint(RequestLimit requestLimit) {
    }

    /**
     *
     * 功能描述:
     *      定义web包目录切点
     * @auther: dison
     * @date: 2018/7/27 13:21
     */
    @Pointcut("execution(public * mn.idax.exchange.otc.web..*.*(..))")
    public void webPoint() {
    }

    /**
     *
     * 功能描述:
     *      环绕通知：
     *          1.拦截切点web包或service包
     *          2.拦截的方法必须含有日志输出注解
     * @param:
     * @return:
     * @auther: dison
     * @date: 2018/7/27 14:06
     */
    @Around("webPoint() && requestLimitPoint(requestLimit)")
    public Object doAround(ProceedingJoinPoint pjp, RequestLimit requestLimit) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        RequestLimit limitAnnotation = method.getAnnotation(RequestLimit.class);
        String key = RequestLimitUtils.getRquestLimitKey(limitAnnotation,method.getName());
        int limitPeriod = limitAnnotation.period();
        int limitCount = limitAnnotation.count();
        ImmutableList<String> keys = ImmutableList.of(StringUtils.join(RedisPrefixKeyEnum.request_limit_prefix_name.getKey(), key));
        String luaScript = RequestLimitUtils.buildLuaScript();
        RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
        Number count = limitRedisTemplate.execute(redisScript, keys, limitCount, limitPeriod);
        if (count != null && count.intValue() <= limitCount) {
            return pjp.proceed();
        } else {
            throw new IdaxException(SystemStatusResponseEnum.REQUEST_LIMIT_ERROR.getCode());
        }
    }


}
